/*

This file is part of Rooms3D.

Copyright (C) 2008 Pixecur Technologies Incorporated (Esotera).
Visit Esotera at http://www.esoteras3.com
 
Rooms3D is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
Rooms3D is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Rooms3D; see the file COPYING.  If not, write to 
Pixecur Technologies, 213-31 Peet Street, St. John's, NL, A1B 3W8. 
 
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
 
*/

package com.datasentinel.j3d.utils.geometry;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.util.Hashtable;

import javax.media.j3d.Appearance;
import javax.media.j3d.ImageComponent;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.Material;
import javax.media.j3d.QuadArray;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Texture;
import javax.media.j3d.Texture2D;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.TransparencyAttributes;
import javax.vecmath.Color3f;

import com.sun.j3d.utils.geometry.Text2D;

public class MutableText2D extends Shape3D
{
	// This table caches FontMetrics objects to avoid the huge cost
	// of re-retrieving metrics for a font we've already seen.
	private static Hashtable<Font, FontMetrics> metricsTable = new Hashtable<Font, FontMetrics>();
	float rectangleScaleFactor = 1f / 256f;

	private Color3f color = new Color3f();
	private String fontName;
	private int fontSize, fontStyle;
	private String text;
	private BufferedImage bImage;
	private Graphics offscreenGraphics;

	public MutableText2D(String text, Color3f color, String fontName,
			int fontSize, int fontStyle)
	{

		this.color.set(color);
		this.fontName = fontName;
		this.fontSize = fontSize;
		this.fontStyle = fontStyle;
		setString(text);

		setupAppearance(null);
//		updateText2D(text, color, fontName, fontSize, fontStyle);

		// super(text, color, fontName, fontSize, fontStyle);
		setCapability(Text2D.ALLOW_GEOMETRY_WRITE);
		setCapability(Text2D.ALLOW_APPEARANCE_WRITE);
	}

	public void setString(String text)
	{
		if(text == null || text.equals("") || (this.text != null && text.equals(this.text)))
			return;
		this.text = text;
		updateText2D(text, color, fontName, fontSize, fontStyle);
//		setStringInternal(text);
//		setRectangleScaleFactor(getRectangleScaleFactor());
	}


	public void setAppearance(Appearance appearance)
	{
		appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE);
		super.setAppearance(appearance);
	}





//	/*
//	 * Changes text of this Text2D to 'text'. All other parameters (color,
//	 * fontName, fontSize, fontStyle remain the same.
//	 * 
//	 * @param text The string to be set.
//	 */
//	private void setStringInternal(String text)
//	{
//		this.text = text;
//
//		int width = 0;
//		int height = 0; 
//
//		Texture tex = getAppearance().getTexture();
//		width = tex.getWidth();
//		height = tex.getHeight();
//		
//		ImageComponent imageComponent = setupImage(text, color, fontName,
//				fontSize, fontStyle);
//		if ((imageComponent.getWidth() == width)
//				&& (imageComponent.getHeight() == height))
//		{
//			tex.setImage(0, imageComponent);
//		}
//		else
//		{
//			Texture2D newTex = setupTexture(imageComponent);
//			// Copy texture attributes except those related to
//			// mipmap since Texture only set base imageComponent.
//
//			newTex.setBoundaryModeS(tex.getBoundaryModeS());
//			newTex.setBoundaryModeT(tex.getBoundaryModeT());
//			newTex.setMinFilter(tex.getMinFilter());
//			newTex.setMagFilter(tex.getMagFilter());
//			newTex.setEnable(tex.getEnable());
//			newTex.setAnisotropicFilterMode(tex.getAnisotropicFilterMode());
//			newTex.setAnisotropicFilterDegree(tex.getAnisotropicFilterDegree());
//			int pcount = tex.getFilter4FuncPointsCount();
//			if (pcount > 0)
//			{
//				float weights[] = new float[pcount];
//				tex.getFilter4Func(weights);
//				newTex.setFilter4Func(weights);
//			}
//			Color4f c = new Color4f();
//			tex.getBoundaryColor(c);
//			newTex.setBoundaryColor(c);
//			newTex.setUserData(tex.getUserData());
//			getAppearance().setTexture(newTex);
//		}
//	}

	private void updateText2D(String text, Color3f color, String fontName,
			int fontSize, int fontStyle)
	{
		ImageComponent imageComponent = setupImage(text, color, fontName,
				fontSize, fontStyle);

		Texture2D t2d = setupTexture(imageComponent);

		QuadArray rect = setupGeometry(imageComponent.getWidth(),
				imageComponent.getHeight(), rectangleScaleFactor);
		setGeometry(rect);

		Appearance appearance = setupAppearance(t2d);
		setAppearance(appearance);
	}


	/**
	 * Sets the scale factor used in converting the image width/height to
	 * width/height values in 3D.
	 * 
	 * @param newScaleFactor
	 *            The new scale factor.
	 */
	public void setRectangleScaleFactor(float newScaleFactor)
	{
		rectangleScaleFactor = newScaleFactor;
		updateText2D(text, color, fontName, fontSize, fontStyle);
	}

	/**
	 * Gets the current scale factor being used in converting the image
	 * width/height to width/height values in 3D.
	 * 
	 * @return The current scale factor.
	 */
	public float getRectangleScaleFactor()
	{
		return rectangleScaleFactor;
	}

	/**
	 * Create the ImageComponent and Texture object.
	 */
	public static Texture2D setupTexture(ImageComponent imageComponent)
	{
		Texture2D t2d = new Texture2D(Texture2D.BASE_LEVEL, Texture.RGBA,
				imageComponent.getWidth(), imageComponent.getHeight());
		t2d.setMinFilter(Texture2D.BASE_LEVEL_LINEAR);
		t2d.setMagFilter(Texture2D.BASE_LEVEL_LINEAR);
		t2d.setImage(0, imageComponent);
		t2d.setEnable(true);
		t2d.setCapability(Texture.ALLOW_IMAGE_WRITE);
		t2d.setCapability(Texture.ALLOW_SIZE_READ);
		t2d.setCapability(Texture.ALLOW_ENABLE_READ);
		t2d.setCapability(Texture.ALLOW_BOUNDARY_MODE_READ);
		t2d.setCapability(Texture.ALLOW_FILTER_READ);
		t2d.setCapability(Texture.ALLOW_BOUNDARY_COLOR_READ);
		t2d.setCapability(Texture.ALLOW_ANISOTROPIC_FILTER_READ);
		t2d.setCapability(Texture.ALLOW_FILTER4_READ);
		return t2d;
	}

	public static FontMetrics getFontMetrics(Font font)
	{
		FontMetrics metrics;
		if ((metrics = (FontMetrics)metricsTable.get(font)) == null)
		{
			metrics = Toolkit.getDefaultToolkit().getFontMetrics(font);
			metricsTable.put(font, metrics);
		}
		return metrics;
	}
	
	public static Dimension getAreaForText(String text, FontMetrics metrics)
	{
		return new Dimension(metrics.stringWidth(text), metrics.getMaxDescent() + metrics.getMaxAscent());
	}
	
	public static BufferedImage createImage(String text, Font font)
	{
		FontMetrics metrics = getFontMetrics(font);
		return createImage(getAreaForText(text, metrics));
	}
	
	public static int nearestGreaterPower(int n)
	{
		int pow = 1;
		for (int i = 1; i < 32; ++i)
		{
			pow <<= 1;
			if (n <= pow)
				return pow;
		}
		return Integer.MAX_VALUE;
	}
	public static BufferedImage createImage(Dimension areaForText)
	{
		// Need to make width/height powers of 2 because of Java3d texture
		// size restrictions
		int width = nearestGreaterPower(areaForText.width);
		int height = nearestGreaterPower(areaForText.height);

		// For now, jdk 1.2 only handles ARGB format, not the RGBA we want
//		if(bImage == null ||
//				(bImage.getWidth() != width || bImage.getHeight() != height))
//		{
		return new BufferedImage(width, height,	BufferedImage.TYPE_INT_ARGB);	
	}
	
	/**
	 * Creates a ImageComponent2D of the correct dimensions for the given font
	 * attributes. Draw the given text into the image in the given color. The
	 * background of the image is transparent (alpha = 0).
	 */
	private ImageComponent setupImage(String text, Color3f color,
			String fontName, int fontSize, int fontStyle)
	{
		Font font = new java.awt.Font(fontName, fontStyle, fontSize);
		bImage = createImage(text, font);
			
			offscreenGraphics = bImage.createGraphics();
//		}
//		else
//		{
////			offscreenGraphics.clearRect(0, 0, width, height);			
//		}

//		Color clearFill = Color.WHITE;// new Color(0f,0f,0f,0f);//  new Color(compArray[0], compArray[1], compArray[2], 0.5f);
//		offscreenGraphics.setColor(clearFill);
		
		
		// First, erase the background to the text panel - set alpha to 0
//		float[] compArray = new float[4];
//		Color.WHITE.getComponents(compArray);
		Color myFill = new Color(0f,0f,0f,0f);//  new Color(compArray[0], compArray[1], compArray[2], 0.5f);
		offscreenGraphics.setColor(myFill);
		offscreenGraphics.fillRect(0, 0, bImage.getWidth(), bImage.getHeight());

		
		
		// Next, set desired text properties (font, color) and draw String
		FontMetrics metrics = (FontMetrics)metricsTable.get(font);
		int descent = metrics.getMaxDescent();
		int ascent = metrics.getMaxAscent();
		int height = descent + ascent;
		offscreenGraphics.setFont(font);
		Color myTextColor = new Color(color.x, color.y, color.z, 1f);
		offscreenGraphics.setColor(myTextColor);
		offscreenGraphics.drawString(text, 0, height - descent);

		
		ImageComponent imageComponent = new ImageComponent2D(
				ImageComponent.FORMAT_RGBA, bImage);

		imageComponent.setCapability(ImageComponent.ALLOW_SIZE_READ);

		return imageComponent;
	}

	/**
	 * Creates a rectangle of the given width and height and sets up texture
	 * coordinates to map the text image onto the whole surface of the rectangle
	 * (the rectangle is the same size as the text image)
	 */
	public static QuadArray setupGeometry(int width, int height, float rectangleScaleFactor)
	{
		float zPosition = 0f;
		float rectWidth = (float)width * rectangleScaleFactor;
		float rectHeight = (float)height * rectangleScaleFactor;
		float[] verts1 = { rectWidth, 0f, zPosition, rectWidth, rectHeight,
				zPosition, 0f, rectHeight, zPosition, 0f, 0f, zPosition };
		float[] texCoords = { 0f, -1f, 0f, 0f, (-1f), 0f, (-1f), -1f };

		QuadArray rect = new QuadArray(4, QuadArray.COORDINATES
				| QuadArray.TEXTURE_COORDINATE_2);
		rect.setCoordinates(0, verts1);
		rect.setTextureCoordinates(0, 0, texCoords);

		return rect;
	}

	/**
	 * Creates Appearance for this Shape3D. This sets transparency for the
	 * object (we want the text to be "floating" in space, so only the text
	 * itself should be non-transparent. Also, the appearance disables lighting
	 * for the object; the text will simply be colored, not lit.
	 */
	public static Appearance setupAppearance(Texture2D t2d)
	{
		TransparencyAttributes transp = new TransparencyAttributes();
		transp.setTransparencyMode(TransparencyAttributes.BLENDED);
		transp.setTransparency(0.0f);
		
		TextureAttributes textureAtts = new TextureAttributes();
//		textureAtts.setTextureMode(TextureAttributes.COMBINE);
		
		
		Appearance appearance = new Appearance();
		appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE);

		
		appearance.setTransparencyAttributes(transp);
		appearance.setTextureAttributes(textureAtts);
		if(t2d != null)
			appearance.setTexture(t2d);

		Material m = new Material();
		m.setLightingEnable(false);
		appearance.setMaterial(m);

		return appearance;
	}

	/**
	 * Returns the text string
	 * 
	 * @since Java 3D 1.2.1
	 */
	public String getString()
	{
		return text;
	}

	/**
	 * Returns the color of the text
	 * 
	 * @since Java 3D 1.2.1
	 */
	public Color3f getColor()
	{
		return color;
	}

	/**
	 * Returns the font
	 * 
	 * @since Java 3D 1.2.1
	 */
	public String getFontName()
	{
		return fontName;
	}

	/**
	 * Returns the font size
	 * 
	 * @since Java 3D 1.2.1
	 */
	public int getFontSize()
	{
		return fontSize;
	}

	/**
	 * Returns the font style
	 * 
	 * @since Java 3D 1.2.1
	 */
	public int getFontStyle()
	{
		return fontStyle;
	}


}
